/****************************************************************************
 * Copyright (C) 2011 Maxim Integrated Products, All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL MAXIM INTEGRATED PRODUCTS BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Maxim Integrated Products
 * shall not be used except as stated in the Maxim Integrated Products
 * Branding Policy.
 *
 * The mere transfer of this software does not imply any licenses
 * of trade secrets, proprietary technology, copyrights, patents,
 * trademarks, maskwork rights, or any other form of intellectual
 * property whatsoever. Maxim Integrated Products retains all ownership rights.
 *
 *     Description: Implements I2C Library Functions
 *             $Id: maxq_i2c.c 5830 2012-11-26 17:29:34Z jeremy.brodt $
 *
 ********************************************************************************
 */

#include "maxq_config.h"
#include "maxq_i2c.h"

/***** Definitions *****/

#define I2C_VERSION      2

// I2CCN bit mask definitions
#define I2C_ENABLE          0x0001 // I2CCN.0:I2CEN bit mask
#define MASTER_MODE         0x0002 // I2CCN.1:I2CMST bit mask
#define MASTER_RECV_MODE    0x0004 // I2CCN.2:I2CMODE bit mask
#define I2C_ACK             0x0020 // I2CCN.5:I2CACK bit mask
#define I2C_START           0x0040 // I2CCN.6:I2CSTART bit mask
#define I2C_STOP            0x0080 // I2CCN.7:I2CSTOP bit mask
#define MASTER_RESET        0x8000 // I2CCN.16:I2CRST bit mask

// I2CST bit mask definitions
#define TRANSMIT_COMPLETE   0x0002 // I2CST.1:I2CTXI bit mask
#define RECEIVE_READY       0x0004 // I2CST.2:I2CRXI bit mask
#define I2C_TIMEOUT         0x0010 // I2CST.4:I2CTOI bit mask
#define I2C_ARB_LOSS        0x0040 // I2CST.6:I2CALI bit mask
#define NACK_RECEIVED       0x0080 // I2CST.7:I2CNACKI bit mask
#define I2C_BUSY            0x4000 // I2CST.14:I2CBUSY bit mask


/***** Function Prototypes *****/
static int i2c_setspeed(unsigned long sysclk, i2c_speed_t i2cspeed);
static void i2c_master_reset(void);


/****************************************************************************/
int i2c_version(void)
{
  return I2C_VERSION;
}

/****************************************************************************/
int i2c_master_init(i2c_speed_t i2cspeed)
{
  I2CCN = 0x0;     // Clear Configuration bits
  I2CST = 0x0;     // Clear Status bits
  I2CIE = 0x0;     // Clear Interrupt enable bits

  I2CTO = 0xFF;    // maximum timeout (at 100 kHZ it is (256 * 1/100) msec

  I2CCN |= (MASTER_MODE | I2C_ENABLE);   // master mode & enable

  return i2c_setspeed(DEFAULT_SYS_CLK_FREQ, i2cspeed);
}

/****************************************************************************/
/* Set I2C controller speed
   sysclk = system clock in Hz
   i2cspeed = desired I2C speed; STD_MODE = 100 kHz, FAST_MODE = 400 kHz,
                                 HS_MODE = 900 kHz
*/
static int i2c_setspeed(unsigned long sysclk, i2c_speed_t i2cspeed)
{
  uint8_t clk;

  switch (i2cspeed) {
    case I2C_STD_MODE:    /* 100 kHZ */
      clk = sysclk / ((unsigned long)100000 * 2);
      break;
    case I2C_FAST_MODE:   /* 400 kHZ */
      clk = sysclk / ((unsigned long)400000 * 2);
      break;
    case I2C_HS_MODE:   /* 900 kHZ */
      clk = sysclk / ((unsigned long)900000 * 2);
      break;
    default:
      return MAXQ_FAILURE;
  }

  I2CCK = (((uint16_t)clk << 8) & 0xff00) | clk;

  return MAXQ_SUCCESS;
}

/****************************************************************************/
/* reset the controller;
   set the bit and wait till the controller clears the bit
 */
static void i2c_master_reset(void)
{
  int count = 0;

  I2CCN |= MASTER_RESET;         // reset the controller
  while(I2CCN & MASTER_RESET) {  // Wait till Controller clears the bit
    if (count++ > 1000) {        // timeout
      i2c_disable();
      i2c_enable();
      break;
    }    
  }

  I2CST = 0x0;      // clear all status bits
}

/****************************************************************************/
int i2c_master_send_byte(uint8_t data)
{
  while (I2CST & I2C_BUSY) {
    if (I2CST & I2C_TIMEOUT) {  // has a timeout occurred?
      i2c_master_reset();
      return MAXQ_FAILURE;
    }
  }

  I2CCN |= MASTER_MODE; // master mode

  I2CBUF = data;        // write data byte

  // Wait till TXI is '1'
  while ((I2CST & TRANSMIT_COMPLETE) != TRANSMIT_COMPLETE) {
    if (I2CST & (I2C_TIMEOUT | I2C_ARB_LOSS)) {  // has a timeout or arbitration loss occurred?
      i2c_master_reset();
      return MAXQ_FAILURE;
    }
  }

  I2CST &= ~TRANSMIT_COMPLETE;  // clear TXI flag

  // we should get ACK/NACK from slave
  if(I2CST & NACK_RECEIVED)    // did a NACK occur?
      return MAXQ_FAILURE;

  return MAXQ_SUCCESS;
}

/****************************************************************************/
int i2c_master_read(uint16_t address, int length, uint8_t *data)
{
  // Set master in receive mode
  I2CCN |= MASTER_RECV_MODE;

  // Prepare to ACK receptions
  I2CCN &= ~I2C_ACK;

  if (i2c_master_start() != MAXQ_SUCCESS)
    return MAXQ_FAILURE;

  if (i2c_master_send_byte(address) != MAXQ_SUCCESS) {
    i2c_master_stop();
    return MAXQ_FAILURE;
  }

  while (length > 0) {

    // If this is the last byte, NACK
    if (length == 1) {
      I2CCN |= I2C_ACK;
    }

    // Wait till RXI is '1'
    while ((I2CST & RECEIVE_READY) != RECEIVE_READY) {
      if (I2CST & I2C_TIMEOUT) {  // has a timeout occurred?
        i2c_master_reset();
        return MAXQ_FAILURE;
      }
    }
  
    *data++ = I2CBUF;         // read data byte
    I2CST &= ~RECEIVE_READY;  // clear RXI flag
    length--;
  }

  return i2c_master_stop();
}

/****************************************************************************/
int i2c_master_write(uint16_t address, int length, uint8_t * data)
{
  // Set master in transmit mode
  I2CCN &= ~MASTER_RECV_MODE;

  if(i2c_master_start() != MAXQ_SUCCESS)
    return MAXQ_FAILURE;

  if (i2c_master_send_byte(address) != MAXQ_SUCCESS) {
    i2c_master_stop();
    return MAXQ_FAILURE;
  }

  while (length-- > 0) {
    if (i2c_master_send_byte(*data++) != MAXQ_SUCCESS) {
      i2c_master_stop();
      return MAXQ_FAILURE;
    }
  }

  return i2c_master_stop();
}

/****************************************************************************/
int i2c_master_start(void)
{
  I2CST = 0x0; // Clear status register

  I2CCN |= I2C_START;

  while (I2CCN & I2C_START) {  // Wait till START bit is cleared
    if (I2CST & I2C_TIMEOUT) { // has a timeout occurred?
      I2CST &= ~I2C_TIMEOUT;
      i2c_master_reset();
      return MAXQ_FAILURE;
    }
  }

  return MAXQ_SUCCESS;
}

/****************************************************************************/
int i2c_master_stop(void)
{
  I2CST = 0x0; // Clear status register

  I2CCN |= I2C_STOP;

  while(I2CCN & I2C_STOP) {   // Wait till STOP bit is cleared
    if(I2CST & I2C_TIMEOUT) { // has a timeout occurred?
      i2c_master_reset();
      return MAXQ_FAILURE;
    }
  }

  return MAXQ_SUCCESS;
}

/****************************************************************************/
void i2c_slave_init(uint16_t slave_address)
{
  I2CCN = 0x0;  // Clear Configuration bits
  I2CST = 0x0;  // Clear Status bits
  I2CIE = 0x0;  // Clear Interrupt enable bits

  I2CCN = I2C_ENABLE;  // slave mode & enable
  I2CSLA = slave_address; // set the slave address
}

/****************************************************************************/
uint8_t i2c_slave_recv(void)
{
  I2CST = 0;  // clear all status
  while (!(I2CST & RECEIVE_READY)); // wait for completion
  return I2CBUF;
}

/****************************************************************************/
int i2c_slave_send(uint8_t data_byte)
{
   I2CST &= ~TRANSMIT_COMPLETE;          // clear Transmit complete flag
   I2CBUF = data_byte;
   while (!(I2CST & TRANSMIT_COMPLETE)); // wait for completion
   return (!!(I2CST & NACK_RECEIVED));   // return ACK state
}

/****************************************************************************/
void i2c_acknowledge(int ackval)
{
  if (ackval)
    I2CCN &= (~I2C_ACK);   // clear I2CACK bit to generate an ACK
  else
    I2CCN |= I2C_ACK;      // set I2CACK bit to generate an NACK
}
